package com.base.lock;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

/**
 * redis阻塞锁
 */
@Configuration
@Aspect
@Order(100)
@Slf4j
public class RedisLockAopConf {

	@Autowired
	private RedisLocker redisLocker;

	@Around("@annotation(redisLock)")
	public Object redisLockAop(ProceedingJoinPoint pjp,RedisLock redisLock) throws Throwable{
		long start = System.currentTimeMillis();
		Object result = null;		//方法调用的返回值
		String key = null;				//Redis 的key
		Object paramValue = null;		//参数的值
		
		Map<String, Object> parameterNames = this.getParameterNames(pjp);//获取方法所有参数
		
		//是否获取到锁了
		boolean hasLock = false;
		
		try {
			
	        //获取参数
	        String paramName =  redisLock.key();
	    	String prefix = redisLock.prefix();
	    	String value = redisLock.value();
	    	boolean noWait = redisLock.noWait();
	    	
	    	if(StringUtils.isEmpty(paramName) && StringUtils.isEmpty(value)){
	    		throw new UnableToAquireLockException("999999","锁不能为空");
	    	}
	    	
	    	if(!StringUtils.isEmpty(value)) {
	    		paramValue = value;
	    	}else if(!StringUtils.isEmpty(paramName)) {
	    		paramValue = this.getRedisLockKey(parameterNames, paramName);
	    	}
	    	
	    	//获取锁的key
	    	key = prefix + paramValue.toString();
	    	
	    	//阻塞锁
	    	if(noWait) {
	    		hasLock = redisLocker.tryLock(key,0);
	    	}else {
	    		hasLock = redisLocker.tryLock(key);
	    	}
	    	
			if(hasLock){
				log.info("获取到Redis锁：{}",key);
				result = pjp.proceed();//调用原方法
			}else{
				log.info("未获取到Redis锁：{}",key);
				throw new UnableToAquireLockException("999999","操作正在进行，请勿频繁执行！");
			}
			
		}finally {
			
			//释放锁
			if(hasLock) {
				redisLocker.unLock(key);
				log.info("释放Redis锁：{}，耗时：{}ms",key,System.currentTimeMillis() - start);
			}
		}
		return result;
	}
	
	/**
	 * 获取lock的key <br>
	 * 格式支持：$userId + 'test' + paramName.attribute
	 * @param parameterNames
	 * @param paramName
	 * @return
	 * @throws Exception
	 */
	private String getRedisLockKey(Map<String, Object> parameterNames,String paramName) throws Exception {
		StringBuilder paramValue = new StringBuilder();
		
		String[] split = paramName.split("\\+");
		for (String p : split) {
			if(!StringUtils.isEmpty(p)) {
				p = p.trim();
				if(p.startsWith("'") && p.endsWith("'")) {
					p = p.substring(1, p.length() - 1);
				}else if(p.equals("$userId")) {//获取当前用户id
					p = "10086";
	    		}else {
	    			Object value2 = this.getParamValue(parameterNames, p);
	    			if(value2 != null) {
	    				p = value2.toString();
	    			}
	    		}
		    	if(p == null || StringUtils.isEmpty(p)){
		    		throw new UnableToAquireLockException("999999","未获取到锁["+paramName+"]");
		    	}
		    	
		    	paramValue.append(p);
			}
		}
		
		return paramValue.toString();
		
	}
	
	/**
	 * 获取属性，如果获取不到，再遍历所有map中value对象
	 */
	private Object getParamValue(Map<String, Object> parameterNames,String paramName) throws Exception{

		Object paramValue = parameterNames.get(paramName);
		
		//根据方法参数名.参数对象的属性名获取指定属性
		if(paramValue == null || StringUtils.isEmpty(paramValue.toString())){
			if(paramName.contains(".")){
				String[] split = paramName.split("\\.");
				if(split.length == 2){
					Object object = parameterNames.get(split[0]);
					if(object != null){
						String firstLetter = split[1].substring(0, 1).toUpperCase();    
			            String getter = "get" + firstLetter + split[1].substring(1); 
			            
						Method method = BeanUtils.findMethodWithMinimalParameters(object.getClass(), getter);
		    			if(method != null){
		    				paramValue = method.invoke(object, new Object[] {});
			    			if(paramValue != null){
			    				return paramValue;
			    			}
		    			}
					}
				}
			}
		}
		//反射获取,目前只支持属性获取，不能获取属性的属性
    	if(paramValue == null || StringUtils.isEmpty(paramValue.toString())){
    		
    		String firstLetter = paramName.substring(0, 1).toUpperCase();    
            String getter = "get" + firstLetter + paramName.substring(1); 
    		for (Object o : parameterNames.values()) {
    			Method method = BeanUtils.findMethodWithMinimalParameters(o.getClass(), getter);
    			if(method != null){
    				paramValue = method.invoke(o, new Object[] {});
	    			if(paramValue != null){
	    				break;
	    			}
    			}
    		}
    	}
    	return paramValue;
	}
	/**
	 * 获取方法的参数
	 */
	private Map<String, Object> getParameterNames(ProceedingJoinPoint pjp) throws NoSuchMethodException, SecurityException{
		
		String[] names = null;//参数名
		Object[] args = pjp.getArgs();//参数值
		
		Signature signature = pjp.getSignature();  
        MethodSignature methodSignature = (MethodSignature) signature;  
	    names = methodSignature.getParameterNames(); 
		
		if(names == null) {
			throw new UnableToAquireLockException("999999","未获取到参数名称列表："+pjp.getTarget().getClass().getName()+"$"+signature.getName());
		}
		
		Map<String, Object> map = new HashMap<>();
		if(names != null && names.length == args.length) {
			for (int i = 0; i < names.length; i++) {
				map.put(names[i], args[i]);
			}
		}
		return map;
	}
}
